import * as THREE from "three";
import * as d3 from "d3";

export default class MapGenerator {
  constructor(scene, config) {
    this.scene = scene;
    this.config = config;
    this.map = new THREE.Object3D();
    this.materials = new Map();
    this.geometries = new Map();
    this.projection = null;
  }

  async loadGeoJson(url) {
    try {
      const loader = new THREE.FileLoader();
      const data = await new Promise((resolve, reject) => {
        loader.load(url, resolve, undefined, reject);
      });
      return JSON.parse(data);
    } catch (error) {
      console.error('加载GeoJSON失败:', error);
      throw error;
    }
  }

  async generateMap(geoJsonData) {
    if (!geoJsonData?.features) {
      throw new Error('无效的GeoJSON数据');
    }

    this.projection = d3.geoMercator()
      .center(this.config.map.center)
      .scale(this.config.map.scale);

    const promises = geoJsonData.features.map((feature, index) => 
      this.createFeature(feature, index)
    );
    
    await Promise.all(promises);
    this.scene.add(this.map);
    return this.map;
  }

  async createFeature(feature, index) {
    const areaGroup = new THREE.Object3D();
    areaGroup.featureIndex = index;
    areaGroup.properties = feature.properties?.name || `区域${index + 1}`;

    const coordinates = feature.geometry.coordinates;
    if (feature.geometry.type === "MultiPolygon") {
      await this.handleMultiPolygon(coordinates, areaGroup);
    } else if (feature.geometry.type === "Polygon") {
      await this.handlePolygon(coordinates, areaGroup);
    }

    this.map.add(areaGroup);
    return areaGroup;
  }

  async handleMultiPolygon(coordinates, parent) {
    for (const polygonGroup of coordinates) {
      for (const polygon of polygonGroup) {
        await this.createAreaMesh(polygon, parent);
      }
    }
  }

  async handlePolygon(coordinates, parent) {
    for (const polygon of coordinates) {
      await this.createAreaMesh(polygon, parent);
    }
  }

  async createAreaMesh(polygon, parent) {
    if (!Array.isArray(polygon) || polygon.length < 3) return;

    const shape = new THREE.Shape();
    polygon.forEach(([lng, lat], i) => {
      const [x, y] = this.projection([lng, lat]);
      if (i === 0) shape.moveTo(x, -y);
      else shape.lineTo(x, -y);
    });

    const geometry = this.getGeometry(shape);
    const material = this.getMaterial();
    
    const mesh = new THREE.Mesh(geometry, material);
    mesh.castShadow = true;
    mesh.receiveShadow = true;
    parent.add(mesh);
  }

  getGeometry(shape) {
    const key = shape.uuid;
    if (!this.geometries.has(key)) {
      const geometry = new THREE.ExtrudeGeometry(shape, this.config.map.extrudeSettings);
      geometry.computeVertexNormals();
      this.geometries.set(key, geometry);
    }
    return this.geometries.get(key);
  }

  getMaterial() {
    const key = 'areaMaterial';
    if (!this.materials.has(key)) {
      const material = new THREE.MeshPhongMaterial({
        color: new THREE.Color(this.config.map.areaColors.top),
        specular: 0x111111,
        shininess: 30,
        transparent: true,
        opacity: 0.95
      });
      this.materials.set(key, material);
    }
    return this.materials.get(key);
  }

  dispose() {
    this.materials.forEach(material => material.dispose());
    this.geometries.forEach(geometry => geometry.dispose());
    this.materials.clear();
    this.geometries.clear();
  }
}